<?php
/**
 * Created by PhpStorm.
 * User: wangsentao
 * Date: 2018/1/30
 * Time: 上午10:46
 */

namespace app\common\ShaoHuoPay\Gateways\Wechat;


use app\common\ShaoHuoPay\Exceptions\InvalidArgumentException;
use app\common\ShaoHuoPay\Exceptions\InvalidSignException;
use app\common\ShaoHuoPay\Exceptions\GatewayException;
use app\common\ShaoHuoPay\tools\ShCollection;
use app\common\ShaoHuoPay\tools\Traits\HasHttpRequest;

class WxTools
{
    use HasHttpRequest;
    /**
     * @var
     */
    private static $instance;

    /**
     * 微信网关
     * @var string
     */
    protected $baseUri = 'https://api.mch.weixin.qq.com/';

    /**
     *
     * @return WxTools
     */
    public static function getInstance()
    {
        if (!(self::$instance instanceof self)) {
            self::$instance = new self();
        }

        return self::$instance;
    }

    /**
     * 请求微信接口
     * @param $endpoint
     * @param $data
     * @param null $key
     * @param null $certClient
     * @param null $certKey
     * @return ShCollection
     */
    public static function requestApi($endpoint, $data, $key = null, $certClient = null, $certKey = null)
    {
        //Log::debug('Request To Wechat Api', [self::baseUri().$endpoint, $data]);

        $result = self::getInstance()->post(
            $endpoint,
            self::toXml($data),
            ($certClient !== null && $certKey !== null) ? ['cert' => $certClient, 'ssl_key' => $certKey] : []
        );
        $result = is_array($result) ? $result : self::fromXml($result);

        if (!isset($result['return_code']) || $result['return_code'] != 'SUCCESS' || $result['result_code'] != 'SUCCESS') {
            throw new GatewayException(
                'Get Wechat API Error:'.$result['return_msg'].($result['err_code_des'] ?? ''),
                20000,
                $result
            );
        }

        if (strpos($endpoint, 'mmpaymkttransfers') !== false || self::generateSign($result, $key) === $result['sign']) {
            return new ShCollection($result);
        }

        //Log::warning('Wechat Sign Verify FAILED', $result);

        throw new InvalidSignException('Wechat Sign Verify FAILED', 3, $result);
    }

    /**
     * 过滤预支付订单
     * @param $payload
     * @param $order
     * @param $config
     * @return array
     */
    public static function filterPayload($payload, $order, $config)
    {
        $payload = array_merge($payload, is_array($order) ? $order : ['out_trade_no' => $order]);

        $type = isset($order['type']) ? ($order['type'].($order['type'] == 'app' ?: '_').'id') : 'app_id';

        $payload['appid'] = $config->get($type, '');

        unset($payload['notify_url'], $payload['trade_type'], $payload['type']);

        $payload['sign'] = self::generateSign($payload, $config->get('key'));

        return $payload;
    }

    /**
     * 生成微信签名字符串
     * @param $data
     * @param null $key
     * @return string
     */
    public static function generateSign($data, $key = null)
    {
        if (is_null($key)) {
            throw new InvalidArgumentException('Missing Wechat Config -- [key]', 1);
        }

        ksort($data);

        $string = md5(self::getSignContent($data).'&key='.$key);

        return strtoupper($string);
    }

    /**
     * 生成签名内容
     * @param $data
     * @return string
     */
    public static function getSignContent($data)
    {
        $buff = '';

        foreach ($data as $k => $v) {
            $buff .= ($k != 'sign' && $v != '' && !is_array($v)) ? $k.'='.$v.'&' : '';
        }

        return trim($buff, '&');
    }

    /**
     * 数组转xml
     * @param $data
     * @return string
     */
    public static function toXml($data)
    {
        if (!is_array($data) || count($data) <= 0) {
            throw new InvalidArgumentException('Convert To Xml Error! Invalid Array!', 2);
        }

        $xml = '<xml>';
        foreach ($data as $key => $val) {
            $xml .= is_numeric($val) ? '<'.$key.'>'.$val.'</'.$key.'>' :
                '<'.$key.'><![CDATA['.$val.']]></'.$key.'>';
        }
        $xml .= '</xml>';
        return $xml;
    }

    /**
     * xml 转数组
     * @param $xml
     * @return mixed
     */
    public static function fromXml($xml)
    {
        if (!$xml) {
            throw new InvalidArgumentException('Convert To Array Error! Invalid Xml!', 3);
        }

        libxml_disable_entity_loader(true);

        return json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA), JSON_UNESCAPED_UNICODE), true);
    }

    /**
     * 微信网关
     * @param null $mode
     * @return string
     */
    public static function baseUri($mode = null)
    {
        switch ($mode) {
            case 'dev':
                self::getInstance()->baseUri = 'https://api.mch.weixin.qq.com/sandboxnew/';
                break;

            case 'hk':
                self::getInstance()->baseUri = 'https://apihk.mch.weixin.qq.com/';
                break;

            default:
                break;
        }

        return self::getInstance()->baseUri;
    }
}